package com.apobates.forum.core.impl.service;

import java.time.LocalDateTime;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.TreeSet;
import java.util.function.BiFunction;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheConfig;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import com.apobates.forum.core.api.dao.BoardDao;
import com.apobates.forum.core.api.dao.BoardGroupDao;
import com.apobates.forum.core.api.service.BoardGroupService;
import com.apobates.forum.core.entity.Board;
import com.apobates.forum.core.entity.BoardGroup;
import com.apobates.forum.utils.Commons;

@Service 
@CacheConfig(cacheNames = "boardgroupCache")
public class BoardGroupServiceImpl implements BoardGroupService{
	@Autowired
	private BoardGroupDao boardGroupDao;
	@Autowired
	private BoardDao boardDao;
	private final static Logger logger = LoggerFactory.getLogger(BoardGroupServiceImpl.class);
	
	@Cacheable(key = "#id", unless="#result==null")
	@Override
	public Optional<BoardGroup> get(int id) { //[BGC]BG1
		if(id>0){
			return boardGroupDao.findOne(Integer.valueOf(id));
		}
		return Optional.empty();
	}
	
	@CacheEvict(key = "#id")
	@Override
	public Optional<Boolean> edit(int id, BoardGroup updateEntity)throws IllegalStateException { //[BGC]BG1-R1
		BoardGroup bg = get(id).orElseThrow(()->new IllegalStateException("版块组(卷)不存在"));
		bg.setTitle(updateEntity.getTitle());
		bg.setDescription(updateEntity.getDescription());
		bg.setStatus(updateEntity.isStatus());
		if(Commons.isNotBlank(updateEntity.getImageAddr())){
			bg.setImageAddr(updateEntity.getImageAddr());
		}
		return boardGroupDao.edit(bg);
	}
	
	@Cacheable
	@Override
	public List<BoardGroup> getAllUsed() { //[BGC]BG2
		return boardGroupDao.findUsed();
	}
	
	@Override
	public List<BoardGroup> getAllUsedAndBoard(boolean containsDefault) { // [BGC]BG3
		BiFunction<BoardGroup, Set<Board>, BoardGroup> action = (bg, bs) -> {
			Optional<BoardGroup> opt = Optional.ofNullable(bg);
			TreeSet<Board> tmp = bs.stream().collect(Collectors.toCollection(() -> new TreeSet<>(Comparator.comparingInt(Board::getRanking))));
			if (!tmp.isEmpty()) {
				opt.ifPresent(tbg->tbg.setBoardes(tmp));
			}
			return opt.orElse(null);
		};
		Map<Integer, Set<Board>> groupBoardData = boardDao.findUsed().collect(Collectors.groupingBy(Board::getVolumesId, Collectors.toSet()));
		List<BoardGroup> groupData = getAllUsed();
		Stream<BoardGroup> result;
		if (containsDefault) {
			result = Stream.concat(groupData.stream(), Stream.of(BoardGroup.getDefault(groupData.size() + 1))); // 排在最后
		} else {
			result = groupData.stream();
		}
		return result.parallel()
				.map(bg -> action.apply(bg, groupBoardData.getOrDefault(bg.getId(), Collections.emptySet())))
				.filter(Objects::nonNull).sorted(Comparator.comparingInt(BoardGroup::getRanking))
				.collect(Collectors.toList());
	}
	
	
	@Override
	public Optional<BoardGroup> getByBoardId(long boardId) {
		return boardGroupDao.findOneByBoard(boardId);
	}
	
	@Override
	public Stream<BoardGroup> getAll() {
		return boardGroupDao.findAll();
	}

	@Override
	public Stream<BoardGroup> getAllContainsDefault() {
		Stream<BoardGroup> data = boardGroupDao.findAll();
		return Stream.concat(Stream.of(BoardGroup.getDefault()), data);
	}

	@Override
	public Optional<BoardGroup> create(String title, String description, String encodeImageAddr, boolean status, int ranking)throws IllegalStateException {
		BoardGroup bg = new BoardGroup(title, description, encodeImageAddr, status, ranking);
		try {
			boardGroupDao.save(bg);
			if(bg.getId()>0) {
				return Optional.of(bg);
			}
		}catch(Exception e) {
			throw new IllegalStateException(e.getMessage());
		}
		throw new IllegalStateException("版块组(卷)创建失败");
	}

	@Override
	public Map<Integer, String> getAllById(Collection<Integer> idList) {
		if(idList==null || idList.isEmpty()){
			return Collections.emptyMap();
		}
		Map<Integer, String> data = boardGroupDao.findAllById(idList).collect(Collectors.toMap(BoardGroup::getId, BoardGroup::getTitle));
		BoardGroup defaultBg = BoardGroup.getDefault();
		data.put(defaultBg.getId(), defaultBg.getTitle());
		return data;
	}
	
	/*栏目(原生的版块组)*/
	@Override
	public Optional<BoardGroup> getOriginByDirectoryNames(String directoryNames) {
		return boardGroupDao.findOneOriginByDirectoryNames(directoryNames);
	}

	@Override
	public Stream<BoardGroup> getAllOrigin() {
		return boardGroupDao.findAllOrigin();
	}
	
	@Override
	public Stream<BoardGroup> getAllOriginById(Collection<Integer> idList) {
		if(null == idList || idList.isEmpty()){
			return getAllOrigin();
		}
		return boardGroupDao.findAllOriginById(idList);
	}

	@Override
	public int buildOrigin(String title, String description, String directoryNames, boolean status)throws IllegalStateException {
		if(!checkOriginDirectNameUnique(directoryNames).orElse(false)){
			throw new IllegalStateException("栏目目录名称唯一性检查失败");
		}
		BoardGroup bg = new BoardGroup();
		bg.setTitle(title);
		bg.setDescription(description);
		bg.setOrigin(true);
		bg.setDirectoryNames(directoryNames); //需要保证唯一
		bg.setStatus(status);
		bg.setEntryDateTime(LocalDateTime.now());
		bg.setRanking(0);
		//
		try {
			boardGroupDao.save(bg);
			if(bg.getId()>0) {
				return bg.getId();
			}
		}catch(Exception e) {
			throw new IllegalStateException(e.getMessage());
		}
		throw new IllegalStateException("栏目创建失败");
	}

	private Optional<Boolean> checkOriginDirectNameUnique(String directoryNames)throws IllegalStateException {
		if(!Commons.isNotBlank(directoryNames)){
			throw new IllegalStateException("栏目的目录名称不可用");
		}
		Integer originId = boardGroupDao.existsOriginDirectoryNames(directoryNames);
		if(originId > 0){
			throw new IllegalStateException("栏目的目录名称已被使用");
		}
		return Optional.of(true);
	}

	@Override
	public Optional<Boolean> checkOriginDirectNameUnique(String directoryNames, int sectionId) throws IllegalStateException{ //编辑栏目时检查目录是否被使用
		if(sectionId <= 0){
			return checkOriginDirectNameUnique(directoryNames);
		}
		//
		if(!Commons.isNotBlank(directoryNames)){
			throw new IllegalStateException("栏目的目录名称不可用");
		}
		Long count = boardGroupDao.existsOriginDirectoryNames(directoryNames, sectionId);
		if(count > 0){
			throw new IllegalStateException("栏目的目录名称已被使用");
		}
		return Optional.of(true);
	}

	@Override
	public Optional<Boolean> editSection(int id, String title, String description, String directoryNames, boolean status)throws IllegalStateException {
		BoardGroup section = getOriginById(id).orElseThrow(()->new IllegalStateException("栏目不存在或暂时不可用"));
		section.setTitle(title);
		section.setDescription(description);
		section.setDirectoryNames(directoryNames);
		section.setStatus(status);
		return boardGroupDao.edit(section);
	}

	@Override
	public Optional<BoardGroup> getOriginById(int sectionId) {
		if(sectionId>0){
			return boardGroupDao.findOneSectionById(sectionId);
		}
		return Optional.empty();
	}
	
}
